﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Cms.Application.Admin.Views;
using Cms.Application.IO;
using Cms.Data;
using Cms.Domain;
using Cms.Domain.RepositoryInterfaces;
using iLib.Web.UI;

namespace Cms.Application.Admin.Presenters
{
    public class MediaPresenter : BasePresenter
    {
        private readonly Administrator _administrator;
        private readonly IMediaView _view;

        public IDiskAccess DiskAccess { get; set; }

        public MediaPresenter(IMediaView view)
            : this(view, new RepositoryFactory())
        {
            DiskAccess = new DiskAccess();
        }
        
        public MediaPresenter(IMediaView view, IRepositoryFactory repositoryFactory)
            : base(view, repositoryFactory)
        {
            _view = view;
            _administrator = AdministratorRepository.GetAdministrator(_view.AdministratorID);
            if (!_administrator.HasPermission.Media)
                _view.RedirectToPermissionDeniedPage();
        }

        public string Initialize()
        {
            _view.SelectedMediaFolderId = MediaRepository.GetMediaFolder(_administrator.SelectedMediaFolderID) != null
                                              ? _administrator.SelectedMediaFolderID
                                              : -1;
            if (!_administrator.CurrentSite.MediaFolders.Any(mf => mf.ID == _view.SelectedMediaFolderId))
                _view.SelectedMediaFolderId = -1;
            SetMediaFolders();
            SetMediaFiles();
            return _administrator.CurrentSite.Domain; // This is used only by Media.aspx for the Pixlr integration
        }
        
        public void SetMediaFiles()
        {
            if (MediaRepository.GetMediaFolder(_view.SelectedMediaFolderId) == null)
                _view.SelectedMediaFolderId = -1;
            if (_view.SelectedMediaFolderId == -1)
                _view.SetMediaFiles(_administrator.CurrentSite.GetRootMediaFiles());
            else
                _view.SetMediaFiles(MediaRepository.GetMediaFolder(_view.SelectedMediaFolderId).Files);
        }
        
        public void SetMediaFolders()
        {
            _administrator.SelectedMediaFolderID = _view.SelectedMediaFolderId;
            AdministratorRepository.SaveAdministrator(_administrator);
            _view.SetMediaFolders(_administrator.CurrentSite.RootMediaFolder, _administrator.CurrentSite.GetRootMediaFolders());
        }

        public bool IsMediaFolderClosed(int mediaFolderId)
        {
            return _administrator.ClosedMediaFolders.Any(id => id == mediaFolderId);
        }

        public void ToggleMediaFolder(int mediaFolderId)
        {
            if (_administrator.ClosedMediaFolders.Contains(mediaFolderId))
            {
                _administrator.ClosedMediaFolders.Remove(mediaFolderId);
            }
            else
            {
                _administrator.ClosedMediaFolders.Add(mediaFolderId);
            }
            AdministratorRepository.SaveAdministrator(_administrator);
            SetMediaFolders();
        }

        public string GetSelectedFolderPath()
        {
            MediaFolder folder = MediaRepository.GetMediaFolder(_view.SelectedMediaFolderId);
            return folder == null ? _administrator.CurrentSite.RootMediaFolder : folder.GetVirtualPath();
        }
        
        public void Sync()
        {
            int i = 0;

            // Delete MediaFolders that doesn't exist on disk from the database
            while (i < _administrator.CurrentSite.MediaFolders.Count)
            {
                if (!Directory.Exists(Url.PhysicalPath + _administrator.CurrentSite.MediaFolders[i].GetPhysicalPath()))
                    DeleteMediaFolder(_administrator.CurrentSite.MediaFolders[i].ID);
                else
                    i++;
            }
            i = 0;

            // Delete MediaFiles that doesn't exist on disk from the database
            while (i < _administrator.CurrentSite.MediaFiles.Count)
            {
                if (!File.Exists(Url.PhysicalPath + _administrator.CurrentSite.MediaFiles[i].GetPhysicalPath()))
                    DeleteMediaFile(_administrator.CurrentSite.MediaFiles[i].ID);
                else
                    i++;
            }

            // Add physical folders and files that doesn't exist in the database
            CheckDirectory(Url.PhysicalPath + _administrator.CurrentSite.RootMediaFolder, string.Empty, null);

            // Update GUI
            SetMediaFolders();
            SetMediaFiles();
        }
        
        public void Move(string draggedId, string dropzoneId)
        {
            MediaFolder targetFolder = dropzoneId == "rootMediaFolder" ? null : MediaRepository.GetMediaFolder(int.Parse(dropzoneId.Substring(1)));
            switch (draggedId.Substring(0, 1))
            {
                case "F":
                    MediaFolder movedFolder = MediaRepository.GetMediaFolder(int.Parse(draggedId.Substring(1)));
                    if (movedFolder != null)
                    {
                        if (movedFolder.IsParentTo(targetFolder))
                        {
                            AddValidationError(Translator.GetText(_view.ContextTranslationFile, "IsParentToTargetError", _view.Localization));
                        }
                        if (!_administrator.CurrentSite.CanMediaFolderBeNamedAndMovedTo(movedFolder.ID, movedFolder.Name, targetFolder))
                        {
                            AddValidationError(Translator.GetText(_view.ContextTranslationFile, "MoveFolder_NameCollisionError", _view.Localization));
                        }
                        if (!IsValid)
                        {
                            break;
                        }
                        string originalPhysicalPath = movedFolder.GetPhysicalPath();
                        IDictionary<int, string> originalPaths = new Dictionary<int, string>();
                        CollectOriginalPathsForUpdate(movedFolder, ref originalPaths);
                        if (targetFolder != null)
                        {
                            if (movedFolder.Parent != null)
                            {
                                movedFolder.Parent.SubFolders.Remove(movedFolder);
                            }
                            targetFolder.SubFolders.Add(movedFolder);
                        }
                        movedFolder.Parent = targetFolder;
                        SiteRepository.SaveSite(movedFolder.Site);
                        DiskAccess.MoveDirectory(originalPhysicalPath, movedFolder.GetPhysicalPath());
                        var frm = new FileReferencesManager(_administrator, SiteRepository, ContentRepository, DiskAccess);
                        foreach (var kvp in originalPaths)
                        {
                            string originalVirtualPath = kvp.Value;
                            _view.RemoveFileFromOutputCache(originalVirtualPath);
                            string newVirtualPath = MediaRepository.GetMediaFile(kvp.Key).GetVirtualPath();
                            frm.UpdateFileReferences(originalVirtualPath, newVirtualPath, _administrator.CurrentSite.ID);
                        }
                    }
                    break;
                case "M":
                    MediaFile movedFile = MediaRepository.GetMediaFile(int.Parse(draggedId.Substring(1)));
                    if (movedFile != null)
                    {
                        if (!_administrator.CurrentSite.CanMediaFileBeNamedAndMovedTo(movedFile.ID, movedFile.Name, targetFolder))
                        {
                            AddValidationError(Translator
                                .GetText(_view.ContextTranslationFile, "MoveFile_NameCollisionError", _view.Localization)
                                .Replace("[[Name]]", movedFile.Name));
                        }
                        if (!IsValid)
                        {
                            break;
                        }
                        string originalPhysicalPath = movedFile.GetPhysicalPath();
                        string originalVirtualPath = movedFile.GetVirtualPath();
                        _view.RemoveFileFromOutputCache(originalVirtualPath);
                        movedFile.Folder = targetFolder;
                        MediaRepository.SaveMediaFile(movedFile);
                        DiskAccess.MoveFile(originalPhysicalPath, movedFile.GetPhysicalPath());
                        var frm = new FileReferencesManager(_administrator, SiteRepository, ContentRepository, DiskAccess);
                        frm.UpdateFileReferences(originalVirtualPath, movedFile.GetVirtualPath(), _administrator.CurrentSite.ID);
                    }
                    SetMediaFiles();
                    break;
            }
            SetMediaFolders();
        }
        
        public void DeleteMediaFolder(int id)
        {
            MediaFolder folder = MediaRepository.GetMediaFolder(id);
            MediaFolder selectedFolder = MediaRepository.GetMediaFolder(_view.SelectedMediaFolderId);
            DiskAccess.DeleteDirectory(folder.GetPhysicalPath());

            // If the folder is selected, move focus to the parent folder instead
            if (folder.ID == _view.SelectedMediaFolderId || folder.IsParentTo(selectedFolder))
            {
                _view.SelectedMediaFolderId = folder.Parent == null ? -1 : folder.Parent.ID;
            }

            // Remove references to files in the folder or its subfolders
            IList<string> virtualPaths = new List<string>();
            CollectOriginalPathsForDelete(folder, ref virtualPaths);
            var frm = new FileReferencesManager(_administrator, SiteRepository, ContentRepository, DiskAccess);
            foreach (string virtualPath in virtualPaths)
            {
                _view.RemoveFileFromOutputCache(virtualPath);
                frm.RemoveFileReferences(virtualPath, _administrator.CurrentSite.ID);
            }
            folder.PrepareRecursiveRemoval();
            SiteRepository.SaveSite(_administrator.CurrentSite);
            SetMediaFolders();
            SetMediaFiles();
        }

        public void DeleteMediaFile(int id)
        {
            MediaFile mediaFile = MediaRepository.GetMediaFile(id);
            _view.RemoveFileFromOutputCache(mediaFile.GetVirtualPath());
            var frm = new FileReferencesManager(_administrator, SiteRepository, ContentRepository, DiskAccess);
            frm.RemoveFileReferences(mediaFile.GetVirtualPath(), _administrator.CurrentSite.ID);
            DiskAccess.DeleteFile(mediaFile.GetPhysicalPath());
            mediaFile.Site.MediaFiles.Remove(mediaFile);
            mediaFile.Site = null;
            if (mediaFile.Folder != null)
            {
                mediaFile.Folder.Files.Remove(mediaFile);
                mediaFile.Folder = null;
            }
            foreach (Menu menu in _administrator.CurrentSite.Menus)
            {
                menu.RemoveImage(mediaFile);
            }
            MediaRepository.DeleteMediaFile(id);
            SiteRepository.SaveSite(_administrator.CurrentSite);
        }
        
        public MediaFile GetMediaFile(int id)
        {
            return MediaRepository.GetMediaFile(id);
        }

        public void Paste()
        {
            foreach (string id in _view.SelectedIDs.Split(",".ToCharArray()))
            {
                MediaFile file = MediaRepository.GetMediaFile(int.Parse(id.Substring(1)));
                if (file == null)
                {
                    continue;
                }
                if (!_administrator.CurrentSite.CanMediaFileBeNamedAndMovedTo(file.ID, file.Name, MediaRepository.GetMediaFolder(_view.SelectedMediaFolderId)))
                {
                    AddValidationError(
                        Translator.GetText(_view.ContextTranslationFile, "MoveFile_NameCollisionError", _view.Localization).Replace("[[Name]]", file.Name));
                }
            }
            if (!IsValid)
            {
                _view.SelectedIDs = string.Empty;
                _view.SelectedGroupAction = string.Empty;
                return;
            }
            foreach (string id in _view.SelectedIDs.Split(",".ToCharArray()))
            {
                MediaFile file = MediaRepository.GetMediaFile(int.Parse(id.Substring(1)));
                if (file == null)
                {
                    continue;
                }
                string originalPhysicalPath = file.GetPhysicalPath();
                switch (_view.SelectedGroupAction)
                {
                    case "cut":
                        if (file.Folder != null)
                        {
                            file.Folder.Files.Remove(file);
                        }
                        string originalVirtualPath = file.GetVirtualPath();
                        _view.RemoveFileFromOutputCache(originalVirtualPath);
                        file.Folder = MediaRepository.GetMediaFolder(_view.SelectedMediaFolderId);
                        var frm = new FileReferencesManager(_administrator, SiteRepository, ContentRepository, DiskAccess);
                        frm.UpdateFileReferences(originalVirtualPath, file.GetVirtualPath(), _administrator.CurrentSite.ID);
                        if (file.Folder != null)
                            file.Folder.Files.Add(file);
                        DiskAccess.MoveFile(originalPhysicalPath, file.GetPhysicalPath());
                        break;
                    case "copy":
                        var clone = (MediaFile)file.Clone();
                        clone.Folder = MediaRepository.GetMediaFolder(_view.SelectedMediaFolderId);
                        if (clone.Folder != null)
                            clone.Folder.Files.Add(clone);
                        DiskAccess.CopyFile(originalPhysicalPath, clone.GetPhysicalPath());
                        break;
                }
            }
            SiteRepository.SaveSite(_administrator.CurrentSite);
            _view.SelectedIDs = string.Empty;
            SetMediaFiles();
        }
        
        public void Zip(string zipFilename)
        {
            if (zipFilename.Trim() == string.Empty)
            {
                AddValidationError(Translator.GetText(_view.ContextTranslationFile, "ZipFile_NoNameError", _view.Localization));
            }
            if (!IsValid)
            {
                return;
            }
            IList<MediaFile> selectedFiles = new List<MediaFile>();
            foreach (string id in _view.SelectedIDs.Split(",".ToCharArray()))
            {
                selectedFiles.Add(MediaRepository.GetMediaFile(int.Parse(id.Substring(1))));
            }
            var zipFile = new MediaFile
                              {
                                  Name = zipFilename + ".zip",
                                  Site = _administrator.CurrentSite,
                                  Extension = "zip",
                                  Folder = MediaRepository.GetMediaFolder(_view.SelectedMediaFolderId)
                              };
            MediaRepository.SaveMediaFile(zipFile);
            DiskAccess.Compress(selectedFiles, zipFile.GetPhysicalPath());
            var fi = new FileInfo(Url.PhysicalPath + zipFile.GetPhysicalPath());
            zipFile.Size = fi.Length;
            MediaRepository.SaveMediaFile(zipFile);
            SetMediaFiles();
            _view.SelectedIDs = string.Empty;
        }
        
        public void Unzip(int zippedMediaFileID)
        {
            MediaFile file = MediaRepository.GetMediaFile(zippedMediaFileID);
            try
            {
                IList<MediaFile> files = DiskAccess.Decompress(file);
                foreach (MediaFile mf in files)
                    MediaRepository.SaveMediaFile(mf);
                SiteRepository.SaveSite(_administrator.CurrentSite);
                SetMediaFiles();
            }
            catch (IOException ex)
            {
                AddValidationError(Translator.GetText(_view.ContextTranslationFile, "Unzip_FileExistsError", _view.Localization).Replace("[[Files]]", ex.Message));
            }
        }

        private void CheckDirectory(string physicalPath, string virtualPath, MediaFolder parent)
        {
            MediaFolder folder = null;

            //Check directory
            if (virtualPath != string.Empty)
            {
                // Skip root media folder
                folder = _administrator.CurrentSite.GetMediaFolder(virtualPath);
                if (folder == null)
                {
                    folder = new MediaFolder
                    {
                        Site = _administrator.CurrentSite,
                        Name = virtualPath.Substring(virtualPath.LastIndexOf("/") + 1),
                        Parent = parent
                    };
                    if (parent != null)
                        parent.SubFolders.Add(folder);
                    _administrator.CurrentSite.MediaFolders.Add(folder);
                    MediaRepository.SaveMediaFolder(folder);
                }
            }
            foreach (string file in Directory.GetFiles(physicalPath))
            {
                MediaFile mediaFile = _administrator.CurrentSite.GetMediaFile(virtualPath, Path.GetFileName(file));
                if (mediaFile == null)
                {
                    mediaFile = new MediaFile
                    {
                        Site = _administrator.CurrentSite,
                        Name = Path.GetFileName(file).Replace(" ", "-")
                    };
                    mediaFile.Extension = mediaFile.Name.Substring(mediaFile.Name.LastIndexOf(".") + 1);
                    mediaFile.Folder = folder;
                    if (folder != null)
                        folder.Files.Add(mediaFile);
                    _administrator.CurrentSite.MediaFiles.Add(mediaFile);
                    var fileInfo = new FileInfo(file);
                    mediaFile.Size = fileInfo.Length;
                    mediaFile.UploadedTimestamp = DateTime.Now;
                    MediaRepository.SaveMediaFile(mediaFile);

                    // Also rename the physical file if the name contains spaces!
                    if (Path.GetFileName(file).IndexOf(" ") >= 0)
                    {
                        string path = _administrator.CurrentSite.RootMediaFolder + "\\";
                        if (virtualPath != string.Empty)
                            path += virtualPath.Replace("/", "\\") + "\\";
                        DiskAccess.MoveFile(path + Path.GetFileName(file), path + mediaFile.Name);
                    }
                }
            }
            foreach (string subfolder in Directory.GetDirectories(physicalPath))
            {
                string virtualSubFolder = subfolder.Replace(Url.PhysicalPath + _administrator.CurrentSite.RootMediaFolder + "\\", string.Empty).Replace("\\", "/");
                CheckDirectory(subfolder, virtualSubFolder, folder);
            }
        }

        private void CollectOriginalPathsForDelete(MediaFolder folder, ref IList<string> paths)
        {
            foreach (MediaFile file in folder.Files)
                paths.Add(file.GetVirtualPath());
            foreach (MediaFolder f in folder.SubFolders)
                CollectOriginalPathsForDelete(f, ref paths);
        }

        private void CollectOriginalPathsForUpdate(MediaFolder folder, ref IDictionary<int, string> paths)
        {
            foreach (MediaFile file in folder.Files)
                paths.Add(file.ID, file.GetVirtualPath());
            foreach (MediaFolder f in folder.SubFolders)
                CollectOriginalPathsForUpdate(f, ref paths);
        }
    }
}